'use strict';

const MysqlClient = require('./mysql_client');
const loader = require('./sql_loader');
const helper = require('./db_helper');

class Database {
  constructor(app) {
    helper.init(app);
    this.app = app;
  }

  /**
   * 根据SQL ID执行SQL
   *
   * @param {string} sqlid SQL ID
   * @param {Object} data 数据
   */
  async execute(sqlid, data) {
    const cfg = loader.load(sqlid);
    if (!cfg) {
      throw new Error('Cannot find sql: ' + sqlid);
    }

    const conn = await helper.getConnection(cfg.ds);
    const client = new MysqlClient(conn, this.app.logger);
    let result = null;
    try {
      result = await client.execute(sqlid, data);
    } catch (err) {
      client.release();
      throw err;
    }
    client.release();
    return result;
  }

  /**
   * 直接执行SQL
   *
   * @param {string} sql SQL
   * @param {Object} data 数据
   * @param {Object} [ds] 数据源
   */
  async executeSql(sql, data, ds) {
    const conn = await helper.getConnection(ds);
    const client = new MysqlClient(conn, this.app.logger);
    let result = null;
    try {
      result = await client.executeSql(sql, data);
    } catch (err) {
      client.release();
      throw err;
    }
    client.release();
    return result;
  }

  /**
   * 执行SQL，参数为数组的方式
   *
   * @param {string} sql SQL
   * @param {Object} datas 数据
   * @param ds.ds
   * @param {Object} [ds] 数据源
   * @param ds.t
   */
  async query(sql, datas, { ds, t }) {
    const client = t || new MysqlClient(await helper.getConnection(ds), this.app.logger);
    let result = null;
    try {
      result = await client.query(sql, datas);
    } catch (err) {
      if (!t) {
        client.release();
      }
      throw err;
    }
    if (!t) {
      client.release();
    }
    return result;
  }

  /**
   * 创建
   *
   * @param {*} table 表名称
   * @param {*} record 待插入的数据对象
   * @param {*} config 其他配置
   * * ds 数据源
   * * t  事务
   */
  async create(table, record = {}, config = {}) {
    const client = config.t || this;
    record = helper.columnFilter(table, record);
    const sql = 'INSERT INTO ' + table + ' SET ${record}';
    return client.executeSql(sql, { record }, config.ds);
  }

  /**
   * 批量创建
   *
   * @param {*} table 表名称
   * @param {*} records 待插入的数据对象列表
   * @param {*} config 其他配置
   * * ds 数据源
   * * t  事务
   */
  async bulkCreate(table, records = [], config = {}) {
    if (records.length === 0) {
      return;
    }
    records = helper.columnFilter(table, records);
    const keys = Object.keys(records[0]);
    const values = records.map(record => {
      return keys.map(key => {
        return record[key];
      });
    });
    const sql = `INSERT INTO ${table} (${keys.join(',')}) VALUES ?`;
    return this.query(sql, [ values ], config);
  }

  /**
   * 更新
   *
   * @param {*} table 表名称
   * @param {*} record 待插入的数据对象列表
   * @param {*} config 其他配置
   * * ds     数据源
   * * t      事务
   * * where  筛选条件对象
   */
  async update(table, record = {}, config = {}) {
    const where = config.where || {};
    const updateCols = [];
    const params = [];

    record = helper.columnFilter(table, record);
    for (const item in record) {
      updateCols.push(`${item} = ?`);
      params.push(record[item]);
    }

    const whereStr = helper.explainWhere(where, config.ds);
    const sql = `UPDATE ${table} SET ${updateCols.join(', ')} ${whereStr}`;
    return this.query(sql, params, config);
  }

  /**
   * 删除
   *
   * @param {*} table 表名称
   * @param {*} config 其他配置
   * * ds 数据源
   * * t  事务
   * * where  筛选条件对象
   */
  async delete(table, config = {}) {
    const where = config.where || {};

    const whereStr = helper.explainWhere(where, config.ds);
    const sql = `DELETE FROM ${table} ${whereStr}`;
    return this.query(sql, [], config);
  }

  /**
   * 单表查询所有记录
   *
   * @param {*} table 表名称
   * @param {*} config 其他配置
   * * ds       数据源
   * * t        事务
   * * where    筛选条件对象，object格式
   * * columns  要查询的字段，不填查所有字段，数组格式
   * * limit    limit限定值，字符串格式
   * * order    排序方式，字符串格式
   * * group    分组方式，字符串格式
   */
  async findAll(table, config = {}) {
    const where = config.where || {};
    const columns = config.columns || [];
    let limitStr = '';
    let orderBy = '';
    let groupBy = '';

    if (config.limit) {
      limitStr = ` LIMIT ${config.limit}`;
    }
    if (config.order) {
      orderBy = ` ORDER BY ${config.order}`;
    }
    if (config.group) {
      groupBy = ` GROUP BY ${config.group}`;
    }


    let columnStr = '*';
    if (columns.length > 0) {
      columnStr = `${columns.join(',')}`;
    }

    const whereStr = helper.explainWhere(where, config.ds);
    const sql = `SELECT ${columnStr} FROM ${table} ${whereStr} ${groupBy} ${orderBy} ${limitStr}`;

    return this.query(sql, [], config);
  }

  /**
   * 单表统计满足条件的行数
   *
   * @param {*} table 表名称
   * @param {*} config 其他配置
   * * ds       数据源
   * * t        事务
   * * where    筛选条件对象，object格式
   */
  async count(table, config = {}) {
    const where = config.where || {};

    const whereStr = helper.explainWhere(where, config.ds);
    const sql = `SELECT COUNT(*) AS cnt FROM ${table} ${whereStr}`;

    const records = await this.query(sql, [], config);
    return records[0].cnt;
  }

  /**
   * 单表查询一条记录
   *
   * @param {*} table 表名称
   * @param {*} config 其他配置
   * * ds       数据源
   * * t        事务
   * * where    筛选条件对象，object格式
   * * columns  要查询的字段，不填查所有字段，数组格式
   */
  async findOne(table, config = {}) {
    const records = await this.findAll(table, { ...config, ...{ limit: '1' } });

    if (records.length > 0) {
      return records[0];
    }
    return null;
  }

  /**
   * 执行事务
   *
   * @param {function} handler 具体执行SQL的钩子函数
   * @param {string} [ds] 数据源
   */
  async trans(handler, ds) {
    const conn = await helper.getConnection(ds);
    await conn.beginTransaction();
    const client = new MysqlClient(conn, this.app.logger);
    try {
      await handler(client);
      await conn.commit();
      client.release();
    } catch (err) {
      await conn.rollback();
      client.release();
      throw err;
    }
  }
}


Database.prototype.P = helper.P;


module.exports = Database;
